#ifndef CLUASTATE_H_
#define CLUASTATE_H_


extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}
#include <Windows.h>

class CLuaState
{
	lua_State * mLuaState;

public:
	//accessors
	inline lua_State * GetState(  )
	{ 
		return mLuaState; 
	}

	//constructor
	CLuaState(  ):mLuaState(lua_open(  ) )
	{
		luaL_openlibs( mLuaState );
	}

	//destructor
	~CLuaState(  )
	{
		lua_close( mLuaState );
	}

	typedef int(*LUAFUNC)(lua_State*);


	void RegisterFunc( const char* funcName, LUAFUNC func )
	{	
		lua_register( mLuaState, funcName, func );
	}

	//execute a lua program
	void DoFile( const char* fileName )
	{	
		if( luaL_dofile( mLuaState, fileName ) )
		{
			const char * error = lua_tostring(mLuaState, -1);
			MessageBox( 0, error, "ERROR DURING DOFILE", MB_OK );
			lua_pop( mLuaState, 1 );
		}	
	}
	//execute a string of lua code
	void DoString( const char* code )
	{	
		if( luaL_dostring( mLuaState, code ) )
		{
			const char * error = lua_tostring(mLuaState, -1);
			MessageBox( 0, error, "ERROR DURING DOSTRING", MB_OK );
			lua_pop( mLuaState, 1 );
		}	
	}

	//call any lua function and catch any returns

	// d == double
	// s == string
	// i == int
	// < == seperate parameters from returns
	void CallLuaFunc( const char* funcName, const char* signature, ... )
	{
		va_list variables;
		va_start( variables, signature );

		int numArgs = 0;
		int numReturns = 0;

		// push the function onto the lua stack
		lua_getglobal( mLuaState, funcName );

		//push the parameters onto the lua stack
		while( *signature)
		{
			switch(*signature++)
			{
			case 'd':
				lua_pushnumber( mLuaState, va_arg(variables, double ) );
				break;
			case 'i':
				lua_pushnumber( mLuaState, va_arg( variables, int ) );
				break;
			case 's':
				lua_pushstring( mLuaState, va_arg( variables, char* ) );
				break;
			case '<':
				goto BEGINRETURNS;
				break;
			default:
				MessageBox( 0, "Invalid parameter type", funcName, MB_OK );
			}
			++numArgs;
		}
		// goto is AMAZING
BEGINRETURNS:
		// find out how many return values there are
		numReturns = strlen(signature);

		if( lua_pcall(mLuaState, numArgs, numReturns, 0 ) )
		{
			const char* err = lua_tostring( mLuaState, -1 );
			MessageBox( 0, "ERROR DURING PCALL", funcName, MB_OK );
			lua_pop( mLuaState, 1 );
		}

		for( int i = 1; i <= numReturns; ++i )
		{
			switch(*signature++)
			{
			case 'd':
				*( va_arg(variables, double*) ) = lua_tonumber(mLuaState, i );
				break;
			case 'i':
				*( va_arg(variables, int*) ) = (int)lua_tonumber(mLuaState, -i );
				break;
			case 's':
				{
					const char* theString= lua_tostring(mLuaState, i );
					char** theVar = va_arg( variables, char** );
					unsigned int len = strlen(theString) + 1;
					*theVar = new char[len];
					strcpy_s( *theVar, len, theString );
				}
				break;
			default:
				MessageBox( 0, "Invalid return type", funcName, MB_OK );			
			}

		}
		lua_pop(mLuaState, numReturns);
		va_end(variables);
	}

};

#endif CLUASTATE_H_